//==============================================================================
//
//             DValue - the value class in the ofc-library
//
//               Copyright (C) 2005  Dick van Oudheusden
//  
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//==============================================================================
// 
//  $Date: 2008-08-13 17:14:32 $ $Revision: 1.3 $
//
//==============================================================================

#include "ofc/DValue.h"


#if _INTERFACE_

#include "ofc/config.h"

#include "ofc/DTextable.h"
#include "ofc/DText.h"
#include "ofc/DBool.h"
#include "ofc/DInt.h"
#include "ofc/DLong.h"
#include "ofc/DDouble.h"


// the value types
#define DVL_EMPTY    0
#define DVL_CLASS    1 // Protocol, category ??
#define DVL_OBJECT   2
#define DVL_SEL      3
#define DVL_BOOL     4
#define DVL_INT      5
#define DVL_LONG     6
#define DVL_DOUBLE   7
#define DVL_STRING   8

//
// The value class implements an union of basic data types. The class stores
// the current type of the storage and the value. This class also implements
// type conversion between the different types of storage.
//
// @example
// #include <stdio.h>
// #include "ofc/DValue.h"
// 
// int main(int argc, char *argv[])
// {
//   DValue *value = [DValue new];
//   DText  *str   = [DText  new];
// 
//   printf("Initial the value %s empty.\n", ([value isEmpty] ? "is" : "is not"));
// 
//   [value setInt :7];                   // Set the value to integer 7
// 
//   printf("Type of value:%s\n", [value typeString]);
// 
//   [str set :"0.07"];                  // Set the value to DText:0.07
//   [value setObject :str];
// 
//   printf("Type of value:%s\n", [value typeString]);
// 
//   printf("Double of value:%f\n", [value toDouble]); // Convert the value to a double
// 
//   printf("Type of value:%s\n", [value typeString]);
// 
//   [value free];                        // Cleanup
//   [str   free];
// 
//   return 0;
// }
//

@interface DValue : Object <DTextable>
{
@private
  int          _type;  // the value type
  union
  {
    Class      _class;  // the class
    id         _object; // the (general) object reference
    SEL        _sel;    // the selector
    BOOL       _bool;   // the boolean
    int        _int;    // the integer
    long       _long;   // the long integer
    double     _double; // the double
    DText     *_text;   // the string
  } _value;
}

#endif



@implementation DValue

static const char *_typeStrings[] = 
{
  "empty",
  "class",
  "object",
  "selector",
  "bool",
  "int",
  "long",
  "double",
  "string"
};


//// Constructors

//
// Initialise to empty value
//
// @return the object
//
- (DValue *) init
{
  [super init];

  _type  = DVL_EMPTY;

  return self;
}


//// Copy related methods

//
// Do a shallow copy of the object
//
// @return the object
//
- shallowCopy
{
  DValue *copy = [super shallowCopy];

  if (_type == DVL_STRING)
  {
    copy->_value._text = [_value._text copy];
  }
  
  return copy;
}


//// Deconstructor

//
// Free the object
//
// @return the object
//
- free
{
  [self empty];

  return [super free];
}


//// Member methods

//
// Empty the value
// 
// @return the object
// 
- (DValue *) empty
{
  if (_type == DVL_STRING)
  {
    [_value._text free];
    
    _value._text = nil;
  }
  
  _type = DVL_EMPTY;
  
  return self;
}

//
// Check if the value is without a value
// 
// @return is the value empty ?
// 
- (BOOL) isEmpty
{
  return (_type == DVL_EMPTY);
}

//
// Return the type of the value
// 
// @return the type of the value (DVL_...)
// 
- (int) type
{
  return _type;
}

//
// Return a string specifying the type of the value
// 
// @return the type string
// 
- (const char *) typeString
{
  const char *txt = NULL;
  
  if ((_type == DVL_CLASS) && (_value._class != Nil))
  {
    txt = [_value._class name];
  }
  else if ((_type == DVL_OBJECT) && (_value._object != nil))
  {
    txt = [_value._object name];
  }
  else if ((_type >= 0) && (_type <= DVL_STRING))
  {
    txt = _typeStrings[_type];
  }
  
  return txt;
}


//// Set methods

// 
// Set the value to a class
// 
// @param value   the class
// 
// @return the object
// 
- (DValue *) setClass :(Class) value
{
  [self empty];

  _type         = DVL_CLASS;
  _value._class = value;
  
  return self;
}

// 
// Set the value to a reference of an object. The object will NOT be
// freed when the value is freed.
// 
// @param value   the reference to the object
// 
// @return the object
// 
- (DValue *) setObject :(id) value 
{
  [self empty];
  
  _type          = DVL_OBJECT;
  _value._object = value;
  
  return self;
}

// 
// Set the value to a selector
// 
// @param value   the selector
// 
// @return the object
// 
- (DValue *) setSel :(SEL) value
{
  [self empty];
  
  _type       = DVL_SEL;
  _value._sel = value;
  
  return self;
}

// 
// Set the value to a boolean
// 
// @param value   the boolean 
// 
// @return the object
// 
- (DValue *) setBool :(BOOL) value
{
  [self empty];
  
  _type        = DVL_BOOL;
  _value._bool = value;
  
  return self;
}

// 
// Set the value to an integer
// 
// @param value   the integer
// 
// @return the object
// 
- (DValue *) setInt :(int) value
{
  [self empty];
  
  _type       = DVL_INT;
  _value._int = value;
  
  return self;
}

// 
// Set the value to a long
// 
// @param value   the long
// 
// @return the object
// 
- (DValue *) setLong :(long) value
{
  [self empty];
  
  _type        = DVL_LONG;
  _value._long = value;
  
  return self;
}

// 
// Set the value to a double
// 
// @param value   the double
// 
// @return the object
// 
- (DValue *) setDouble :(double) value
{
  [self empty];
  
  _type          = DVL_DOUBLE;
  _value._double = value;
  
  return self;
}

// 
// Set the value to a c-string
// 
// @param value   the c-string
// 
// @return the object
// 
- (DValue *) setString :(const char *) value
{
  if (_type != DVL_STRING)
  {
    [self empty];
  
    _type        = DVL_STRING;
    
    _value._text = [DText new];
  }
  
  [_value._text set :value];
  
  return self;
}


//// Get methods

// 
// Return the class in the value
// 
// @return the class (or Nil)
// 
- (Class) getClass 
{
  Class value = Nil;
  
  if (_type != DVL_CLASS)
  {
    WARNING(DW_UNEXPECTED_ERROR, "type mismatch");
  }
  else
  {
    value = _value._class;
  }
  
  return value;
}

// 
// Return the reference in the value
// 
// @return the object (or nil)
// 
- (id) getObject
{
  id value = nil;
  
  if (_type != DVL_OBJECT)
  {
    WARNING(DW_UNEXPECTED_ERROR, "type mismatch");
  }
  else
  {
    value = _value._object;
  }
  
  return value;
}

// 
// Return the selector in the value
// 
// @return the selector (or NULL)
// 
- (SEL) getSel
{
  SEL value = NULL;
  
  if (_type != DVL_SEL)
  {
    WARNING(DW_UNEXPECTED_ERROR, "type mismatch");
  }
  else
  {
    value = _value._sel;
  }
  
  return value;
}

// 
// Return the boolean in the value
// 
// @return the boolean (or NO)
// 
- (BOOL) getBool
{
  BOOL value = NO;
  
  if (_type != DVL_BOOL)
  {
    WARNING(DW_UNEXPECTED_ERROR, "type mismatch");
  }
  else
  {
    value = _value._bool;
  }
  
  return value;
}

// 
// Return the integer in the value
// 
// @return the integer (or 0)
// 
- (int) getInt
{
  int value = 0;
  
  if (_type != DVL_INT)
  {
    WARNING(DW_UNEXPECTED_ERROR, "type mismatch");
  }
  else
  {
    value = _value._int;
  }
  
  return value;
}

// 
// Return the long in the value
// 
// @return the long (or 0)
// 
- (long) getLong
{
  long value = 0L;
  
  if (_type != DVL_LONG)
  {
    WARNING(DW_UNEXPECTED_ERROR, "type mismatch");
  }
  else
  {
    value = _value._long;
  }
  
  return value;
}

// 
// Return the double in the value
// 
// @return the double (or 0.0)
// 
- (double) getDouble
{
  double value = 0.0;
  
  if (_type != DVL_DOUBLE)
  {
    WARNING(DW_UNEXPECTED_ERROR, "type mismatch");
  }
  else
  {
    value = _value._double;
  }
  
  return value;
}

// 
// Return the c-string in the value
// 
// @return the c-string  (or NULL)
// 
- (const char *) getString 
{
  const char *value = NULL;
  
  if (_type != DVL_STRING)
  {
    WARNING(DW_UNEXPECTED_ERROR, "type mismatch");
  }
  else
  {
    value = [_value._text cstring];
  }
  
  return value;
}


//// Conversion methods

// 
// Convert the value to a class: Nil for selector, boolean, integer, long, 
// double and string.
// 
// @return the class (or Nil)
// 
- (Class) toClass 
{
  Class value = Nil;
  
  switch (_type)
  {
    case DVL_CLASS:
      value = _value._class;
      break;
    
    case DVL_OBJECT:
      value = (_value._object != nil) ? [_value._object class] : Nil;
      break;
  }
  
  return value;
}

// 
// Convert the value to a (new) object: a copy for an object reference, nil for selector,
// DBool object for boolean, DInt object for integer, DLong object for long, DDouble object
// for double and DText for string.
// 
// @return the (new) object (or nil)
// 
- (id) toObject
{
  id obj = nil;
  
  switch (_type)
  {
    case DVL_CLASS:
      obj = (_value._class  != Nil) ? [_value._class new] : nil;
      break;
    
    case DVL_OBJECT:
      obj = (_value._object != nil) ? [_value._object copy] : nil;
      break;
    
    case DVL_BOOL:
      {
        DBool *tmp = [DBool alloc];
      
        [tmp init :_value._bool];
      
        obj = tmp;
      }
      break;
    
    case DVL_INT:
      {
        DInt *tmp = [DInt alloc];
      
        [tmp init :_value._int];
      
        obj = tmp;
      }
      break;
    
    case DVL_LONG:
      {
        DLong *tmp = [DLong alloc];
     
        [tmp init :_value._long];
       
        obj = tmp;
      }
      break;
    
    case DVL_DOUBLE:
      {
        DDouble *tmp = [DDouble alloc];
      
        [tmp init :_value._double];
       
        obj = tmp;
      }
      break;
 
    case DVL_STRING:
      obj = (_value._text != nil) ? [_value._text copy] : nil;
      break;
  }
  
  return obj;
}

// 
// Convert the value to a selector: NULL is returned if value is not a selector
// 
// @return the selector (or NULL)
// 
- (SEL) toSel
{
  if (_type == DVL_SEL)
  {
    return _value._sel;
  }
  else
  {
    return NULL;
  }
}

// 
// Convert the value to a boolean: class and sel returns NO, object tries toBool, int, long and
// double return true if not 0/NULL, string is scanned for contents
// 
// @return the boolean (or NO)
// 
- (BOOL) toBool
{
  BOOL value = NO;
  
  switch(_type)
  {
    case DVL_OBJECT:
      if ((_value._object != nil) && ([_value._object respondsTo :@selector(toBool)]))
      {
        value = [_value._object toBool];
      }
      break;

    case DVL_BOOL:
      value = _value._bool;
      break;
    
    case DVL_INT:
      value = (_value._int != 0);
      break;
    
    case DVL_LONG:
      value = (_value._long != 0L);
      break;
    
    case DVL_DOUBLE:
      value = (!(_value._double < 0.0) || (_value._double > 0.0));
      break;
      
    case DVL_STRING:
      if (_value._text != nil)
      {
        DBool *tmp = [DBool new];
        char  *txt = (char *) [_value._text cstring];
        
        if ([tmp fromString :&txt] == 0)
        {
          value = [tmp get];
        }
        
        [tmp free];
      }
      break;
  }
  
  return value;
}

// 
// Convert the value to an integer: class and sel return 0, object tries toInt,
// bool, long and double are type casted to int, string is read for an int
// 
// @return the integer (or 0)
// 
- (int) toInt
{
  int value = 0;
  
  switch(_type)
  {
    case DVL_OBJECT:
      if ((_value._object != nil) && ([_value._object respondsTo :@selector(toInt)]))
      {
        value = [_value._object toInt];
      }
      break;
    
    case DVL_BOOL:
      value = (int) _value._bool;
      break;
    
    case DVL_INT:
      value = _value._int;
      break;
    
    case DVL_LONG:
      value = (int) _value._long;
      break;
    
    case DVL_DOUBLE:
      value = (int) _value._double;
      break;
    
    case DVL_STRING:
      value = (_value._text != nil) ? [_value._text toInt] : 0;
      break;
  }
  
  return value;
}

// 
// Convert the value to a long: class and sel return 0, object tries toLong,
// bool, int and double are type casted to long, string is read for an int
// 
// @return the long (or 0)
// 
- (long) toLong
{
  long value = 0L;
  
  switch(_type)
  {
    case DVL_OBJECT:
      if ((_value._object != nil) && ([_value._object respondsTo :@selector(toLong)]))
      {
        value = [_value._object toLong];
      }
      break;
    
    case DVL_BOOL:
      value = (long) _value._bool;
      break;
    
    case DVL_INT:
      value = (long)_value._int;
      break;
    
    case DVL_LONG:
      value = _value._long;
      break;
    
    case DVL_DOUBLE:
      value = (long) _value._double;
      break;
    
    case DVL_STRING:
      value = (_value._text != nil) ? [_value._text toLong] : 0L;
      break;
  }
  
  return value;
}

// 
// Convert the value to a double: class and sel return 0, object tries toDouble,
// bool, int and long are type casted to double, string is read for an double
// 
// @return the double (or 0.0)
// 
- (double) toDouble
{
  double value = 0.0;
  
  switch(_type)
  {
    case DVL_OBJECT:
      if ((_value._object != nil) && ([_value._object respondsTo :@selector(toDouble)]))
      {
        value = [_value._object toDouble];
      }
      break;
    
    case DVL_BOOL:
      value = (double) _value._bool;
      break;
    
    case DVL_INT:
      value = (double) _value._int;
      break;
    
    case DVL_LONG:
      value = (double) _value._long;
      break;
    
    case DVL_DOUBLE:
      value = _value._double;
      break;
    
    case DVL_STRING:
      value = (_value._text != nil) ? [_value._text toDouble] : 0.0;
      break;
  }
  
  return value;
}

// 
// Convert the value to a string: class and sel return nil, object tries toText,
// bool, int, long and double do toText, string is copied
// 
// @return a (new) text objecct
// 
- (DText *) toText
{
  DText *text = nil;
  
  switch(_type)
  {
    case DVL_OBJECT:
      if ((_value._object != nil) && ([_value._object respondsTo :@selector(toText)]))
      {
        text = [_value._object toText];
      }
      break;
    
    case DVL_BOOL:
      {
        DBool *tmp = [DBool alloc];
      
        [tmp init :_value._bool];
      
        text = [tmp toText];
      
        [tmp free];
      }
      break;
    
    case DVL_INT:
      {
        DInt *tmp = [DInt alloc];
      
        [tmp init :_value._int];
      
        text = [tmp toText];
      
        [tmp free];
      }
      break;
    
    case DVL_LONG:
      {
        DLong *tmp = [DLong alloc];
      
        [tmp init :_value._long];
      
        text = [tmp toText];
      
        [tmp free];
      }
      break;
    
    case DVL_DOUBLE:
      {
        DDouble *tmp = [DDouble alloc];
      
        [tmp init :_value._double];
      
        text = [tmp toText];
      
        [tmp free];
      }
      break;
    
    case DVL_STRING:
      text = (_value._text != nil) ? [_value._text copy] : nil;
      break;
  }
  
  return text;
}

@end

/*===========================================================================*/

